var Pjs2;
(function (Pjs2) {
    var Parser = (function () {
        function Parser() {
            var _this = this;
            this.keywords = ["function", "if"];
            this.operators = [[","], ["*", "/"], ['+', '-'], ["|"], ["="]];
            this.unaryOperators = ["++", "--", "!"];
            this.keywordParsers = {
                "if": function (script, pos) {
                    var condition = _this.ReadParameterValue(script, pos);
                    trim(script, pos);
                    if (script.charAt(pos.pos) != "{")
                        throw _this.incompleteScriptError;
                    pos.pos++;
                    var ifScript = _this.ReadScript(script, pos);
                    pos.pos++;
                    var elseScript = new Pjs2.Script();
                    trim(script, pos);
                    if (isHere(script, pos, "else")) {
                        pos.pos += "else".length;
                        trim(script, pos);
                        if (script.charAt(pos.pos) != "{")
                            throw "invalid_format: '{' expected";
                        pos.pos++;
                        elseScript = _this.ReadScript(script, pos);
                    }
                    return new Pjs2.Condition(condition, ifScript, elseScript);
                },
                "function": function (script, pos) {
                    trim(script, pos);
                    var fname = _this.ReadString(script, pos, false).value;
                    var parameters = new Array();
                    trim(script, pos);
                    if (script.charAt(pos.pos) == "(") {
                        pos.pos++;
                        trim(script, pos);
                        while (script.charAt(pos.pos) != ")") {
                            if (script.charAt(pos.pos) == "$") {
                                pos.pos++;
                                var ns = pos.pos;
                                while (charIsLetterNumberOr(script.charAt(pos.pos), "_$^")) {
                                    pos.pos++;
                                }
                                var name = script.substring(ns, pos.pos);
                                parameters.push(new Pjs2.FuncParameterDefinition(name));
                            } else
                                throw "invalid token, '$' expected";
                            trim(script, pos);
                            if (script.charAt(pos.pos) == ",")
                                pos.pos++;
                            else if (script.charAt(pos.pos) != ")")
                                throw "',' or ')' expected at position " + pos.pos;
                            trim(script, pos);
                        }
                        pos.pos++;
                    }
                    trim(script, pos);
                    var s = _this.ReadScriptBlock(script, pos).script;
                    return new Pjs2.DefineFunction(Pjs2.FunctionDefinition.FromScript(s, fname, parameters));
                }
            };
        }
        Object.defineProperty(Parser.prototype, "incompleteScriptError", {
            get: function () {
                return "incomplete_script_error";
            },
            enumerable: true,
            configurable: true
        });
        Object.defineProperty(Parser.prototype, "invalidFormat", {
            get: function () {
                return "invalid_format";
            },
            enumerable: true,
            configurable: true
        });

        Parser.prototype.ReadScript = function (script, pos) {
            if (typeof pos === "undefined") { pos = new Pos(script.length - 1); }
            var result = new Pjs2.Script();
            var start = pos.pos;
            while (pos.pos < pos.endpos) {
                trim(script, pos);
                if (script.charAt(pos.pos) == "}")
                    break;
                if (script.charAt(pos.pos) == ")")
                    throw "invalid_format: ')' occured in the script and no one knows how to parse it";
                if (script.charAt(pos.pos) == "\n") {
                    pos.pos++;
                    continue;
                }
                result.commands.push(this.ReadCommand(script, pos));
            }
            result.source = script.substring(start, pos.pos);
            return result;
        };

        Parser.prototype.ReadCommand = function (script, pos) {
            if (typeof pos === "undefined") { pos = new Pos(script.length - 1); }
            var last = null;
            do {
                trim(script, pos);
                var expr = true;

                //jestli zaciname pismenkem, tak to bude asi prikaz
                if (charIsLetter(script.charAt(pos.pos)) || charIsNumber(script.charAt(pos.pos))) {
                    expr = false;
                    var ns = pos.pos;
                    while (!charIs(script.charAt(pos.pos), " (){}|\n;") && pos.pos <= pos.endpos)
                        pos.pos++;
                    var str = script.substring(ns, pos.pos);

                    //kdyz nazev neni validni cislo, tak precteme prikaz
                    if (isNaN(parseFloat(str))) {
                        if (this.keywords.some(function (s) {
                            return s == str;
                        })) {
                            last = this.ReadKeywordBlock(str, script, pos);
                        } else {
                            var params = this.ReadParameters(script, pos);
                            last = new Pjs2.Call(str, params, last);
                        }
                    } else {
                        expr = true;
                        pos.pos = ns;
                    }
                }

                //jestli ne zkusime vyraz
                if (expr)
                    last = this.ReadExpression(script, pos);
                //zopakujeme si to jestli je tu pipeline
            } while(script.charAt(pos.pos++) == "|");
            pos.pos--;

            return last;
        };

        Parser.prototype.ReadExpression = function (script, pos, readTo) {
            if (typeof pos === "undefined") { pos = new Pos(script.length - 1); }
            if (typeof readTo === "undefined") { readTo = ""; }
            var ops = new Array();
            while (true) {
                trim(script, pos);
                var actual;
                var cast = new Array();
                var operator = "";

                //precist operator
                if (script.charAt(pos.pos) == "-") {
                    //napr -eq -lt -gt ...
                    pos.pos++;
                    var ns = pos.pos;
                    while (charIsLetter(script.charAt(pos.pos)) && !charIs(script.charAt(pos.pos), readTo)) {
                        pos.pos++;
                    }
                    operator = "-" + script.substring(ns, pos.pos);
                } else {
                    //napr. + - * / , | && ||
                    var ns = pos.pos;
                    while (!charIs(script.charAt(pos.pos), " \n\0({$@#\"'[" + readTo) && !charIsLetter(script.charAt(pos.pos)) && !charIsNumber(script.charAt(pos.pos))) {
                        pos.pos++;
                    }
                    operator = script.substring(ns, pos.pos);
                }

                trim(script, pos);

                while (script.charAt(pos.pos) == "[") {
                    pos.pos++;
                    var path = new Array();
                    while (script.charAt(pos.pos) != "]") {
                        path.push(this.ReadString(script, pos, false, ".]").value);
                        if (script.charAt(pos.pos) == ".")
                            pos.pos++;
                        else if (script.charAt(pos.pos) != "]")
                            throw "']' or '.' expected at " + pos.pos;
                    }
                    pos.pos++;
                    trim(script, pos);
                    actual = new Pjs2.StaticClass(path);
                    if (script.charAt(pos.pos) == ":" && script.charAt(pos.pos + 1) == ":") {
                        pos.pos += 2;
                        actual = this.ReadDotNotation(script, actual, pos);
                    } else if (charIsLetterNumberOr(script.charAt(pos.pos), "[({$")) {
                        var c = "";
                        path.forEach(function (p, i) {
                            if (i != 0)
                                c += ".";
                            c += p;
                        });
                        cast.push(c);
                    }
                }

                //přečíst vnořený příkaz
                if (script.charAt(pos.pos) == "(") {
                    pos.pos++;
                    actual = this.ReadCommand(script, pos);
                    if (script.charAt(pos.pos) != ")")
                        throw "invalid brackets";
                    pos.pos++;
                } else if (script.charAt(pos.pos) == "{") {
                    pos.pos++;
                    var r = this.ReadScript(script, pos);
                    if (script.charAt(pos.pos) != "}")
                        throw "invalid curly brackets";
                    pos.pos++;
                    actual = new Pjs2.ScriptBlockCreate(r);
                } else if (script.charAt(pos.pos) == "\"" || script.charAt(pos.pos) == "'") {
                    actual = this.ReadString(script, pos);
                } else if (charIsNumber(script.charAt(pos.pos))) {
                    var ns = pos.pos;
                    while (charIsNumber(script.charAt(pos.pos)) || script.charAt(pos.pos) == ".") {
                        pos.pos++;
                    }
                    if (charIsLetter(script.charAt(pos.pos)))
                        throw "invalid_number";
                    var num = parseFloat(script.substring(ns, pos.pos));
                    actual = new Pjs2.NumberCreate(num);
                } else if (script.charAt(pos.pos) == "$") {
                    pos.pos++;
                    var ns = pos.pos;
                    while (charIsLetterNumberOr(script.charAt(pos.pos), "_$^")) {
                        pos.pos++;
                    }
                    var name = script.substring(ns, pos.pos);
                    actual = new Pjs2.VariableCall(name);
                } else if (script.charAt(pos.pos) == "@") {
                    pos.pos++;
                    if (script.charAt(pos.pos) == "{") {
                        pos.pos++;
                        actual = this.ReadHashtable(script, pos);
                    } else
                        throw "not_supported_or_invalid_command";
                }

                if (actual == null)
                    throw "not_supported_or_invalid_command";

                trim(script, pos);

                while (script.charAt(pos.pos) == ".") {
                    pos.pos++;
                    actual = this.ReadDotNotation(script, actual, pos);
                }

                if (script.charAt(pos.pos) == "=") {
                    pos.pos++;
                    trim(script, pos);
                    var next = this.ReadExpression(script, pos);
                    if (actual instanceof Pjs2.VariableCall) {
                        return new Pjs2.VariableSet(actual.name, next, cast[0]);
                    } else if (actual instanceof Pjs2.Property) {
                        return new Pjs2.PropertySet(actual, next);
                    }
                }

                cast.forEach(function (c) {
                    actual = new Pjs2.Cast(actual, c);
                });
                ops.push({ op: operator, val: actual });
                trim(script, pos);
                if (charIs(script.charAt(pos.pos), ")}|\n;\0" + readTo) || pos.pos >= pos.endpos)
                    break;
            }

            return this.OperatorsToCommand(ops);
        };

        Parser.prototype.ReadScriptBlock = function (script, pos) {
            trim(script, pos);
            if (script.charAt(pos.pos) == "{") {
                pos.pos++;
                var r = this.ReadScript(script, pos);
                if (script.charAt(pos.pos) != "}")
                    throw "invalid curly brackets";
                pos.pos++;
                return new Pjs2.ScriptBlockCreate(r);
            }
            throw "'{' expected at position " + pos.pos;
        };

        Parser.prototype.ReadString = function (script, pos, quoted, disabledChars) {
            if (typeof pos === "undefined") { pos = new Pos(script.length - 1); }
            if (typeof quoted === "undefined") { quoted = true; }
            if (typeof disabledChars === "undefined") { disabledChars = ""; }
            var ech = script.charAt(pos.pos);
            if (!charIs(ech, "\"'")) {
                if (quoted)
                    throw "can't read quoted string";
                else
                    ech = " \0\n;(){}|";
            } else {
                quoted = true;
                pos.pos++;
            }
            ech += disabledChars;
            var escape = false;
            var res = "";
            while (!charIs(script.charAt(pos.pos), ech) || escape) {
                var c = script.charAt(pos.pos);
                if (pos.pos > pos.endpos)
                    break;
                if (c == "")
                    break;
                if (escape) {
                    if (c == "`")
                        res += "`";
                    else if (c == '"')
                        res += '"';
                    else if (c == "'")
                        res += "'";
                    else if (c == "n")
                        res += "\n";
                    else if (c == "r")
                        res += "\r";
                    else if (c == "0")
                        res += "\0";
                    else
                        res += c;
                    escape = false;
                } else if (c == "`")
                    escape = true;
                else if (charIs(c, ech))
                    break;
                else
                    res += c;
                pos.pos++;
            }
            if (!charIs(script.charAt(pos.pos), ech) && quoted)
                throw "incomplete_string";
            if (quoted)
                pos.pos++;
            return new Pjs2.StringCreate(res);
        };

        Parser.prototype.OperatorsToCommand = function (ops) {
            if (ops.length == 0)
                return null;
            if (ops.length == 1) {
                if (ops[0].op == "")
                    return ops[0].val;
                else
                    return new Pjs2.UnaryOperator(ops[0].op, ops[0].val);
            }
            if (ops[0].op != "") {
                ops[0] = { op: "", val: new Pjs2.UnaryOperator(ops[0].op, ops[0].val) };
            }

            this.operators.forEach(function (o) {
                var actualOp = null;
                for (var i = 0; i < ops.length; i++) {
                    if (i != 0) {
                        if (ops[i].op == "" && i != 0)
                            throw "invalid_fromat";
                        if (actualOp == null) {
                            if ($.inArray(ops[i].op, o) >= 0)
                                actualOp = new Pjs2.Operator(ops[i].op, [ops[i - 1].val, ops[i].val]);
                        } else if (ops[i].op == actualOp.operator) {
                            actualOp.commands.push(ops[i].val);
                        } else if ($.inArray(actualOp.operator, o) >= 0) {
                            //pocatecnimu prvku aktualni grupy priradit zgrupovanou verzi
                            ops[i - actualOp.commands.length].val = actualOp;

                            //posunout pozici za ní
                            i -= actualOp.commands.length - 1;

                            //a oriznout to zbyle
                            ops.splice(i, actualOp.commands.length - 1);

                            //aktualni bude zase {prave udelana grupa} {operator na i} {element na i}
                            actualOp = new Pjs2.Operator(ops[i].op, [ops[i - 1].val, ops[i].val]);
                        } else {
                            //pocatecnimu prvku aktualni grupy priradit zgrupovanou verzi
                            ops[i - actualOp.commands.length].val = actualOp;

                            //posunout pozici za ní
                            i -= actualOp.commands.length - 1;

                            //a oriznout to zbyle
                            ops.splice(i, actualOp.commands.length - 1);

                            //aktualni bude zase {prave udelana grupa} {operator na i} {element na i}
                            actualOp = null;
                        }
                    }
                }
                if (actualOp != null) {
                    ops[ops.length - actualOp.commands.length].val = actualOp;
                    ops.splice(ops.length - actualOp.commands.length + 1, actualOp.commands.length - 1);
                }
            });
            return ops[0].val;
        };

        Parser.prototype.ReadDotNotation = function (script, on, pos) {
            if (typeof pos === "undefined") { pos = new Pos(script.length - 1); }
            var ns = pos.pos;
            while (charIsLetterNumberOr(script.charAt(pos.pos), "_")) {
                pos.pos++;
            }
            var name = script.substring(ns, pos.pos);
            trim(script, pos);
            if (script.charAt(pos.pos) == "(") {
                var params = this.ReadMethodParameters(script, pos);
                pos.pos++;
                return new Pjs2.MethodCall(on, name, params);
            }
            return new Pjs2.Property(on, name);
        };

        Parser.prototype.ReadMethodParameters = function (script, pos) {
            if (script.charAt(pos.pos) == "(") {
                pos.pos++;
            } else
                throw "'(' expected at position " + pos.pos;
            var params = new Array();
            while (!charIs(script.charAt(pos.pos), ");\n}")) {
                if (script.charAt(pos.pos) == ",")
                    pos.pos++;
                trimc(script, pos, " \n\t");
                params.push(this.ReadExpression(script, pos, "),"));
                trim(script, pos);
            }
            return params;
        };

        Parser.prototype.ReadParameters = function (script, pos) {
            var result = {};
            var parameterPosition = 0;
            while (pos.pos < pos.endpos) {
                trim(script, pos);
                var name = "_pos_" + parameterPosition;
                var value;

                //když tu končí nějaké závorky nebo příkaz, tak ukončit čtení
                if (charIs(script.charAt(pos.pos), ")}|\n;"))
                    break;

                //jestli je tu jméno přečíst ho
                if (script.charAt(pos.pos) == "-") {
                    var ns = ++pos.pos;
                    while (charIsLetter(script.charAt(pos.pos)))
                        pos.pos++;
                    name = script.substring(ns, pos.pos);
                    while (script.charAt(pos.pos) == " " && pos.pos <= pos.endpos)
                        pos.pos++;
                }

                //pokud tu neni takove pismenko (konec prikazu, konec skriptu nebo zacatek jmena), precist hodnotu
                if (!charIs(script.charAt(pos.pos), ")}|\n;-")) {
                    value = this.ReadParameterValue(script, pos);
                }
                result[name] = value;
            }
            return result;
        };

        Parser.prototype.ReadParameterValue = function (script, pos) {
            if (typeof pos === "undefined") { pos = new Pos(script.length - 1); }
            trim(script, pos);

            //když začíná písmenkem, číslem nebo dolarem přešíst zvláštním způsobem
            if (charIsLetter(script.charAt(pos.pos)) || charIsNumber(script.charAt(pos.pos)) || charIs(script.charAt(pos.pos), '$/*-+_')) {
                var ns = pos.pos;
                while (!charIs(script.charAt(pos.pos), " (){}|\n;") && pos.pos <= pos.endpos)
                    pos.pos++;
                var str = script.substring(ns, pos.pos);
                var num;
                if (!isNaN(num = parseFloat(str)))
                    return new Pjs2.NumberCreate(num);
                var regexp = /(([0-9]+)|(\$.*)|(\(.*\))(\+)|(\-)|(\*)|(\/)|,)/;
                if (str.match(regexp)) {
                    return this.ReadExpression(str, new Pos(str.length - 1), " ");
                } else
                    return new Pjs2.StringCreate(str);
            }

            //jinak přečíst normálně jako příkaz
            return this.ReadCommand(script, pos);
        };

        Parser.prototype.ReadHashtable = function (script, pos) {
            if (typeof pos === "undefined") { pos = new Pos(script.length - 1); }
            var fields = new Array();
            trim(script, pos);
            while (script.charAt(pos.pos) != '}') {
                // read key
                var name = this.ReadString(script, pos, false, ";=");
                trim(script, pos);
                if (script.charAt(pos.pos) != "=")
                    throw "invalid hashtable format: '=' expected";
                pos.pos++;
                trim(script, pos);

                //read value
                var value = this.ReadExpression(script, pos);

                trim(script, pos);
                if (script.charAt(pos.pos) == ";" || script.charAt(pos.pos) == "\n")
                    pos.pos++;
                else if (script.charAt(pos.pos) != "}")
                    throw "invalid hashtable format: ';' or '}' expected";

                fields.push({ name: name, value: value });
                trim(script, pos);
            }
            return new Pjs2.NewHashtable(fields);
        };

        Parser.prototype.ReadKeywordBlock = function (word, script, pos) {
            if (typeof pos === "undefined") { pos = new Pos(script.length - 1); }
            return this.keywordParsers[word](script, pos);
        };
        return Parser;
    })();
    Pjs2.Parser = Parser;
})(Pjs2 || (Pjs2 = {}));
//# sourceMappingURL=pjs2-parser.js.map
